package errorx

import (
	"fmt"

	"github.com/pkg/errors"
	"google.golang.org/grpc/status"
)

type Error struct {
	code uint32
	msg  string
}

func New(code uint32, msg string) *Error {
	return &Error{
		code: code,
		msg:  msg,
	}
}

func NewMsg(msg string) *Error {
	return &Error{
		code: 7,
		msg:  msg,
	}
}

func (e *Error) WithMsg(format string, args ...interface{}) *Error {
	reason := fmt.Sprintf(format, args...)
	msg := e.msg + ": reason - " + reason
	return New(e.code, msg)
}

func (e *Error) SetMsg(format string, args ...interface{}) *Error {
	reason := fmt.Sprintf(format, args...)
	msg := e.msg + ": reason - " + reason
	return New(e.code, msg)
}

func (e *Error) GetCode() uint32 {
	return e.code
}

func (e *Error) GetMsg() string {
	return e.msg
}

func (e *Error) Error() string {
	return fmt.Sprintf("code:%d,msg:%s", e.code, e.msg)
}

func IsError(err error, target *Error) bool {
	e := FromError(err)
	if e.code == target.code {
		return true
	}
	return false
}

// FromError try to convert an error to *Error.
func FromError(err error) *Error {
	if err == nil {
		return nil
	}

	causeErr := errors.Cause(err)
	// 如果在api层重新定义的自定义错误类型，则直接返回给前端
	if e, ok := causeErr.(*Error); ok {
		return New(e.GetCode(), e.GetMsg())
		// 如果是rpc层返回的grpc错误类型，则先获取错误码，然后判断是否为自定义错误类型转换成的grpc错误类型
		// 如果是自定义错误类型转换成的grpc错误类型，则取出自定义错误码和错误信息，并返回给前端
	} else if grpcStatus, ok := status.FromError(causeErr); ok {
		grpcCode := uint32(grpcStatus.Code())
		if grpcCode != 0 {
			return New(grpcCode, grpcStatus.Message())
		}
	}

	return ServerError
}

func NewErrCodeMsg(code uint32, format string, args ...interface{}) *Error {
	return &Error{code, fmt.Sprintf(format, args...)}
}
